-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix windows Socket::connect_timeout
overflow
#112464
Fix windows Socket::connect_timeout
overflow
#112464
Conversation
r? @thomcc (rustbot has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
bb125c5
to
80b5ea8
Compare
@rustbot review |
26720ce
to
845c89c
Compare
This looks good to me.
This is a bit more tricky but I think the easiest way would be to copy/paste |
This comment has been minimized.
This comment has been minimized.
1e6a863
to
e23346b
Compare
This comment has been minimized.
This comment has been minimized.
e23346b
to
2953b71
Compare
This comment has been minimized.
This comment has been minimized.
2953b71
to
fc4cff2
Compare
This comment has been minimized.
This comment has been minimized.
fc4cff2
to
7bac07d
Compare
7bac07d
to
11df849
Compare
library/std/src/net/tcp/tests.rs
Outdated
@@ -46,6 +46,22 @@ fn connect_error() { | |||
} | |||
} | |||
|
|||
#[test] | |||
fn connect_timeout_error() { | |||
match TcpStream::connect_timeout(&"0.0.0.0:1".parse().unwrap(), Duration::MAX) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you confirmed this test fails without the changes in net.rs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eval-exec for an alternative idea of what a test could do:
- bind some free port on localhost using
TcpListener::bind
, then - connect to that new port with
Duration::MAX
timeout.
Here's a draft in Rust:
use std::net::TcpListener;
let listener = TcpListener::bind("127.0.0.1:0").unwrap(); // :0 picks some free port
let port = listener.local_addr().unwrap().port(); // obtain the port it picked
[..]
[..] format!("127.0.0.1:{port}").as_str() [..] // connect to that port
I have a real example of something very close at /~https://github.com/hartwork/rust-for-it/blob/4e51982c4465c3190d92a59747c5d2738baf9e07/src/network.rs#L160-L177 .
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, I'm testing it on a win10 virtual machine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eval-exec I checked the latest version: 0.0.0.0:1
will only work if you start a TCP listener on port 1. And port 1 only works if it's unoccupied and you have privileges because ports up to 1024 are reserved. In a test context you'll probably want a dynamic free port as in my example above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, I try to connect to an unreachable address 8.8.8.8:9999
, but it didn't get timeout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eval-exec updated twice more (in case you only saw the version sent via notification e-mail)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be format!("0.0.0.0:{port}"
or format!("127.0.0.1:{port}"
for connect_timeout
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eval-exec I would argue for 127.0.0.1 for clarity. Both work in practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I should bind
and accept
in another thread:
#[test]
fn connect_timeout_ok_bind() {
let socket_addr = next_test_ip4();
thread::spawn({
let listener = t!(TcpListener::bind(&socket_addr));
move || {
let mut stream = t!(listener.accept()).0;
let mut buf = [0];
t!(stream.read(&mut buf));
}
});
assert!(TcpStream::connect_timeout(&socket_addr, Duration::MAX).is_ok());
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I should
bind
andaccept
in another thread:
@eval-exec that's not necessary, see /~https://github.com/hartwork/rust-for-it/blob/4e51982c4465c3190d92a59747c5d2738baf9e07/src/network.rs#L160-L177 .
Chris seems like he's reviewing this. r? @ChrisDenton |
11df849
to
7871e56
Compare
This comment has been minimized.
This comment has been minimized.
a4cce33
to
ca83467
Compare
This comment has been minimized.
This comment has been minimized.
@ChrisDenton yes.
Cannot confirm, it caused infinite blocking for me as documented at #112405.
Cannot confirm, no error should be returned but success since this is about available targets. |
The failure condition in your issue was "Instead, this happened: the call never succeeds, not even for google.com:80 on Windows." That is not infinite blocking. |
@ChrisDenton maybe I should have put it more clearly: by "never succeeds" I meant that it should have returned success quickly for google.com:80 but instead it blocked forever and hence never finishes to return with due success. Given your spot-on early analysis at #112405 (comment) I do not understand how we have different understanding now. I'd be happy to learn what I a missing now. Are there two bugs here instead of a single one? |
@ChrisDenton PS: The CI at /~https://github.com/hartwork/rust-for-it/actions/runs/5204380736/jobs/9388510541 was cancelled by GitHub after near-exact 6 hours runtime due to that call while google.com:80 was available. |
What's the related code of this canceled CI action? |
@eval-exec that would effectively be /~https://github.com/hartwork/rust-for-it/tree/4c816e51eb8134cef32571400f5583e3f75cec54 or: git clone /~https://github.com/hartwork/rust-for-it
cd rust-for-it
git checkout 4c816e51eb8134cef32571400f5583e3f75cec54 |
connect to a reachable address:use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
fn main() {
let address_result = "1.1.1.1:80".to_socket_addrs();
let address = address_result.unwrap().next().unwrap();
TcpStream::connect_timeout(&address, Duration::MAX).expect("connect_timeout");
println!("main fn returned");
}
connect to an unreachable address:use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
fn main() {
let address_result = "1.1.1.1:9999".to_socket_addrs();
let address = address_result.unwrap().next().unwrap();
TcpStream::connect_timeout(&address, Duration::MAX).expect("connect_timeout");
println!("main fn returned");
}
|
So, I think I can add a unit test: connect to an unreachable address like #[test]
fn connect_timeout_to_unreachable_address() {
let now = Instant::now();
let result =
TcpStream::connect_timeout(&format!("1.1.1.1:9999").parse().unwrap(), Duration::MAX);
assert!(matches!(result, ErrorKind::TimedOut));
assert!(now.elapsed() > Duration::from_secs(20));
} What do you think? @hartwork @ChrisDenton |
@hartwork the reason your CI ran indefinitely is because you have an infinite loop. if running only the code in the issue, it errors immediately. |
13c995c
to
bf52e9b
Compare
This comment has been minimized.
This comment has been minimized.
Signed-off-by: Eval EXEC <execvy@gmail.com>
bf52e9b
to
f65b5d0
Compare
library/std/src/net/tcp/tests.rs
Outdated
@@ -46,6 +46,37 @@ fn connect_error() { | |||
} | |||
} | |||
|
|||
#[test] | |||
fn connect_timeout_to_unreachable_address() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested, this unit test will cost at 2 minutes and 8 seconds on Linux platform, and cost 20 seconds on Windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we only need one test here. We only need to focus on testing the problematic code path in one way. 2 minutes and 8 secs is a long time for a single unit test so I'd prefer if we just used connect_timeout_error
test and removed the other two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think 2 minutes and 8 seconds is a long time too.
@ChrisDenton thanks for clearing this up, you're 100% right, I was able to confirm your results in GitHub Actions now. Sorry 🙏 |
Co-authored-by: Chris Denton <christophersdenton@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great to me, thanks! Sorry testing end up being so involved.
@bors r+ rollup |
Thank you.
…On Tue, Jun 20, 2023 at 6:57 PM Chris Denton ***@***.***> wrote:
***@***.**** approved this pull request.
This looks great to me, thanks! Sorry testing end up being so involved.
—
Reply to this email directly, view it on GitHub
<#112464 (review)>,
or unsubscribe
</~https://github.com/notifications/unsubscribe-auth/ALCAINR7SJLSDNMQPA2SILLXMF6ZRANCNFSM6AAAAAAZAVVJSE>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
…llaumeGomez Rollup of 6 pull requests Successful merges: - rust-lang#112464 (Fix windows `Socket::connect_timeout` overflow) - rust-lang#112720 (Rustdoc: search: color item type and reduce size to avoid clashing) - rust-lang#112762 (Sort the errors from arguments checking so that suggestions are handled properly) - rust-lang#112786 (change binders from tuple structs to named fields) - rust-lang#112794 (Fix linker failures when #[global_allocator] is used in a dependency) - rust-lang#112819 (Disable feature(unboxed_closures, fn_traits) in weird-exprs) r? `@ghost` `@rustbot` modify labels: rollup
This PR want to close #112405